/*
* Copyright 2009 NOR_/D Labs <http://labs.nortd.com>
*
* This file is part of SceneExpression.
* SceneExpression is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License 
* as published by the Free Software Foundation, either version 3 of 
* the License, or (at your option) any later version. For details
* see <http://www.gnu.org/licenses/>.
*
* * *
* 
*/

#include <sx/Widget.h>

using namespace sx;


Widget::Widget() {
    _bDragging = false;
    
    // setup the background geometry
    _backgroundGeometry = new osg::Geometry();
    _backgroundVertices = new osg::Vec3Array;       
    _backgroundGeometry->setVertexArray(_backgroundVertices.get());
    //
    _backgroundColors = new osg::Vec4Array;
    _backgroundColors->push_back(osg::Vec4(0.0f, 0.0f, 0.0f, 0.7f));
    _backgroundGeometry->setColorArray(_backgroundColors.get());
    _backgroundGeometry->setColorBinding(osg::Geometry::BIND_OVERALL);
    //
    _backgroundNormals = new osg::Vec3Array;
    _backgroundNormals->push_back(osg::Vec3(0.0f,0.0f,1.0f));
    _backgroundGeometry->setNormalArray(_backgroundNormals.get());
    _backgroundGeometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
    //
    _backgroundDrawArray = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_FAN, 
                                               0, _backgroundVertices->size());
    _backgroundGeometry->addPrimitiveSet(_backgroundDrawArray.get());
    _geode->addDrawable( _backgroundGeometry.get() );
    
    // setup the border geometry - reuse background vertices, normals
    _borderGeometry = new osg::Geometry();
    _borderGeometry->setVertexArray(_backgroundVertices.get());
    //
    _borderColors = new osg::Vec4Array;
    _borderColors->push_back(osg::Vec4(0.7f, 0.7f, 0.7f, 0.4f));
    _borderGeometry->setColorArray(_borderColors.get());
    _borderGeometry->setColorBinding(osg::Geometry::BIND_OVERALL);
    //
    _borderGeometry->setNormalArray(_backgroundNormals.get());
    _borderGeometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
    //
    _borderDrawArray = new osg::DrawArrays(osg::PrimitiveSet::LINE_LOOP, 
                                           0, _backgroundVertices->size());
    _borderGeometry->addPrimitiveSet(_borderDrawArray.get());
    _geode->addDrawable( _borderGeometry.get() );
        
    // geometry for picking
    // shares the same vertices but can be transformed 
    // via an extra PositionAttitudeTransform
    _pickGeode = new osg::Geode();
    _pickGeode->addDrawable( _backgroundGeometry.get() );         
    _pickXform = new osg::PositionAttitudeTransform();
    _pickXform->addChild(_pickGeode.get());
    _pickXform->setCullCallback(new ForceCullCallback());  // allwyas cull, invisible     
    _xform->addChild(_pickXform.get());
    //leave a ref to this in geode
    //so we have a reference to this when the geode is picked
    _pickGeode->setUserData(this);    

    // enable the call to the update(...) method  during updateTraversal
    enableUpdateCallback();
    
    //state
    disableLighting();
    disableDepthTest();
    enableBlending();
    
    //state - line
    osg::StateSet* stateset = _geode->getOrCreateStateSet();
    stateset->setMode(GL_LINE_SMOOTH, osg::StateAttribute::ON);
    osg::LineWidth* lw = new osg::LineWidth(2.5);
    stateset->setAttributeAndModes(lw, osg::StateAttribute::ON);    
    
}

void Widget::reshape() {
    // This method is supposed to be overwritted
    // by subclasses. The following is just here as
    // an example and defines a widthxheight rectangle
    // as the background geometry.
    _backgroundVertices->clear();
    setBackgroundColor(0.0f, 0.0f, 0.0f, 0.7f);
    _backgroundVertices->push_back(osg::Vec3(0.0f, 0.0f, 0.0f));
    _backgroundVertices->push_back(osg::Vec3(0.0f, _height, 0.0f));
    _backgroundVertices->push_back(osg::Vec3(_width, _height, 0.0f));
    _backgroundVertices->push_back(osg::Vec3(_width, 0.0f, 0.0f));
    _backgroundDrawArray->setCount(_backgroundVertices->size());
}    

void Widget::setName( std::string name) {
    _geode->setName(name);
}
std::string Widget::getName() {
    return _geode->getName();
}
void Widget::setZIndex(long zIndex) {
    _zIndex = zIndex;        
}
long Widget::getZIndex() {
    if( !_bDragging ) {
        return _zIndex;
    } else {
        //make sure wigets gets touchMove events first
        return _zIndex + 10000;
    }
}
void Widget::setShape( float width, float height) {
    _width = width;    
    _height = height;
    reshape();    
}   
float Widget::getWidth() {
    return _width;
}   
float Widget::getHeight() {
    return _height;
}   

void Widget::setBackgroundColor( osg::Vec4 color ) {
    _backgroundColors->clear();
    _backgroundColors->push_back(color);
    _backgroundGeometry->dirtyDisplayList();
}
void Widget::setBackgroundColor( float r, float g, float b, float a ) {
    setBackgroundColor(osg::Vec4(r,g,b,a));
}
osg::Vec4 Widget::getBackgroundColor() {
    return *(_backgroundColors->begin());
}

void Widget::setBorderColor( osg::Vec4 color ) {
    _borderColors->clear();
    _borderColors->push_back(color);
    _borderGeometry->dirtyDisplayList();
}
void Widget::setBorderColor( float r, float g, float b, float a ) {
    setBorderColor(osg::Vec4(r,g,b,a));
}
osg::Vec4 Widget::getBorderColor() {
    return *(_borderColors->begin());
}

    
void Widget::setXY( const osg::Vec3& pnt ) {  // convinience method for setting pos in the x,y plane
    const osg::Vec3d& pos = getPosition(); 
    setPosition(pnt[0], pnt[1], pos[2]);    
}
void Widget::addXY( const osg::Vec3& pnt ) {  // convinience method for adding to pos in the x,y plane
    const osg::Vec3d& pos = getPosition(); 
    setPosition(pos[0]+pnt[0], pos[1]+pnt[1], pos[2]);    
}    

bool Widget::isDragging( Touch& touch ) {
    if( _touchDownLocalPosition.find(&touch) != _touchDownLocalPosition.end() ) {    
        return true;
    } else {
        return false;
    }
}
osg::Vec3 Widget::getDragDelta( Touch& touch ) {
    velocity = touch.getLocalPosition() - _touchDownLocalPosition[&touch];   
    return velocity;
}
osg::Vec3 Widget::getWorldDragDelta( Touch& touch ) {
    osg::Matrix xformMat;
    _xform->computeLocalToWorldMatrix(xformMat, NULL);
    return osg::Matrixd::transform3x3(getDragDelta( touch ), xformMat);
}



void Widget::setPosition( float x, float y, float z) {
    _xform->setPosition(osg::Vec3(x, y, z));
}


bool Widget::touchDown( Touch& touch ) {
    std::cout << "touchDown on: " << getName() << std::endl;
    return true;
}  
bool Widget::touchMove( Touch& touch ) {
    std::cout << "touchMove on: " << getName() << std::endl;
    return true;
}
bool Widget::touchUp( Touch& touch ) {
    std::cout << "touchUp on: " << getName() << std::endl;
    return true;
}


bool Widget::touchDownInternal( Touch& touch ) {
    _touchDownLocalPosition[&touch] = touch.getLocalPosition();
    return touchDown(touch);
}  
bool Widget::touchMoveInternal( Touch& touch ) {
    return touchMove(touch);
}
bool Widget::touchUpInternal( Touch& touch ) {
    bool ret = touchUp(touch);
    _touchDownLocalPosition.erase(&touch);
    return ret;
}




// Misc
//
void Widget::addRoundedRect( float x, float y, float w, float h, float r, osg::Vec3Array* vertices ) {
    vertices->push_back(osg::Vec3( x+r, y, 0.0f));
    vertices->push_back(osg::Vec3( x+w-r, y, 0.0f));
    roundedCorner( x+w-r, y, x+w-r, y+r, vertices );
    vertices->push_back(osg::Vec3( x+w, y+r, 0.0f));
    vertices->push_back(osg::Vec3( x+w, y+h-r, 0.0f));
    roundedCorner( x+w, y+h-r, x+w-r, y+h-r, vertices );
    vertices->push_back(osg::Vec3( x+w-r, y+h, 0.0f));
    vertices->push_back(osg::Vec3( x+r, y+h, 0.0f));
    roundedCorner( x+r, y+h, x+r, y+h-r, vertices );
    vertices->push_back(osg::Vec3( x, y+h-r, 0.0f));
    vertices->push_back(osg::Vec3( x, y+r, 0.0f));
    roundedCorner( x, y+r, x+r, y+r, vertices );
    vertices->push_back(osg::Vec3( x+r, y, 0.0f));
}
void Widget::roundedCorner( float x, float y, float pivotX, float pivotY, osg::Vec3Array* vertices ) {
    float a = osg::PI*0.125;  //0.5*PI is 90deg
    vertices->push_back(osg::Vec3( ((x-pivotX)*cos(a) - (y-pivotY)*sin(a)) + pivotX, 
                                   ((x-pivotX)*sin(a) + (y-pivotY)*cos(a)) + pivotY,
                                   0.0f  ));
    a = osg::PI*0.1875;
    vertices->push_back(osg::Vec3( ((x-pivotX)*cos(a) - (y-pivotY)*sin(a)) + pivotX, 
                                   ((x-pivotX)*sin(a) + (y-pivotY)*cos(a)) + pivotY,
                                   0.0f  ));
    a = osg::PI*0.25;
    vertices->push_back(osg::Vec3( ((x-pivotX)*cos(a) - (y-pivotY)*sin(a)) + pivotX, 
                                   ((x-pivotX)*sin(a) + (y-pivotY)*cos(a)) + pivotY,
                                   0.0f  ));
    a = osg::PI*0.3125;
    vertices->push_back(osg::Vec3( ((x-pivotX)*cos(a) - (y-pivotY)*sin(a)) + pivotX, 
                                   ((x-pivotX)*sin(a) + (y-pivotY)*cos(a)) + pivotY,
                                   0.0f  ));
    a = osg::PI*0.375;
    vertices->push_back(osg::Vec3( ((x-pivotX)*cos(a) - (y-pivotY)*sin(a)) + pivotX, 
                                   ((x-pivotX)*sin(a) + (y-pivotY)*cos(a)) + pivotY,
                                   0.0f  ));
    a = osg::PI*0.4375;
    vertices->push_back(osg::Vec3( ((x-pivotX)*cos(a) - (y-pivotY)*sin(a)) + pivotX, 
                                   ((x-pivotX)*sin(a) + (y-pivotY)*cos(a)) + pivotY,
                                   0.0f  ));
}



float Widget::rangemap(float r1val, float r1min, float r1max, float r2min, float r2max) {
    /**
    *                     r1val
    *    |- - - - -|--------x----|
    *    0       r1min       \  r1max
    *                         \
    *    |- - - - - -|---------x-------|
    *    0         r2min      return  r2max
    */
    return (r1val - r1min) * ((r2max - r2min)/(r1max - r1min)) + r2min;
}
